﻿using Microsoft.International.Converters.PinYinConverter;
using System.Collections.Generic;
using System.Globalization;
using System.Text.Json;

namespace BootstrapBlazorApp.OnlyServer;

public static class YuePortObjectExtensions
{
    /// <summary>
    /// Continue of 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="obj"></param>
    /// <param name="handle"></param>
    /// <returns></returns>
    public static T HandleWhenNotNull<T>(this T obj, Action<T> handle, Action elseHandle = null) where T : class
    {
        if (obj == default)
        {
            elseHandle?.Invoke();
            return obj;
        }

        handle(obj);
        return obj;
    }

    public static T? HandleWhenNotNullOrDefault<T>(this T? value, Action<T> handle, Action elseHandle = null) where T : struct
    {
        if (!value.HasValue)
        {
            elseHandle?.Invoke();
            return value;
        }

        handle(value.Value);
        return value;
    }

    public static TOutput TransformWhenNotNull<T, TOutput>(this T obj, Func<T, TOutput> transform, TOutput defaultOutput = default) where T : class
    {
        if (obj == default)
        {
            return defaultOutput;
        }
        return transform(obj);
    }

    public static void HandleWhenIsOf<T>(this object obj, Action<T> handle) where T : class
    {
        if (obj == null) return;
        if (obj is T)
        {
            handle(obj as T);
        }
    }

    public static void WhenHasValue<T>(this T? obj, Action<T> handle, Action elseHandle = null) where T : struct
    {
        if (obj == null || !obj.HasValue)
        {
            elseHandle?.Invoke();
            return;
        }
        handle(obj.Value);
    }

    public static void WhenNotHasValue<T>(this T? obj, Action<T> handle) where T : struct
    {
        if (obj != null || obj.HasValue) return;
        handle(obj.Value);
    }

    public static void WhenNotNullOrWhiteSpace(this string str, Action<string> handle)
    {
        if (string.IsNullOrWhiteSpace(str)) return;
        handle(str);
    }

    public static void WhenNullOrWhiteSpace(this string str, Action<string> handle)
    {
        if (!string.IsNullOrWhiteSpace(str)) return;
        handle(str);
    }

    public static string ToStringOrDefault(this object obj, string defaultValue = null)
    {
        if (obj == null) return defaultValue;
        return obj.ToString();
    }

    public static bool ToBooleanOrDefault(this bool? obj, bool defaultValue = false)
    {
        if (!obj.HasValue) return defaultValue;
        return obj.Value;
    }
    public static bool? ToBooleanOrDefault(this object obj)
    {
        if (obj == null) return null;

        return obj.ToValue<bool>();
    }

    public static DateTime? ToDateTimeOrDefault(this object obj)
    {
        if (obj == null) return null;

        return obj.ToValue<DateTime?>();
    }

    public static byte ToByteOrDefault(this object obj, byte defaultValue = default)
    {
        if (obj == null) return defaultValue;
        return Convert.ToByte(obj.ToString());
    }

    public static int ToIntOrDefault(this int? obj, int defaultValue = 0)
    {
        if (!obj.HasValue) return defaultValue;
        return obj.Value;
    }

    public static string ToString(this object obj, string formatString, string defaultValue = null)
    {
        if (obj == null) return defaultValue;
        return string.Format("{0:" + formatString + "}", obj);
    }

    public static T ToDecoratedWhenNotNull<T>(this T obj, Func<T, T> decorate) where T : class
    {
        if (obj == null)
        {
            return obj;
        }
        return decorate(obj);
    }


    public static T ToValueOrDefault<T>(this T? nullableObject, T defaultValue = default) where T : struct
    {
        if (nullableObject == null)
        {
            return defaultValue;
        }
        return nullableObject ?? defaultValue;
    }
}

public static class Extension
{
    public static string ToPinYin(this string input, bool first = true)
    {
        var regex = new System.Text.RegularExpressions.Regex(@"[\u4e00-\u9fa5]");
        if (!regex.IsMatch(input))
            return input;

        return string.Join("",
            input
            .ToCharArray()
            .Select(x => x > 133
                ? new ChineseChar(x).Pinyins.Select(x => x.Substring(0, first ? 1 : x.Length - 1)).First()
                : x.ToString()
            )
        );
    }
}

public static class DataConvert
{
    public static T ToValue<T>(this object obj, T defaultValue)
    {
        return (T)ToValue(obj, CultureInfo.InvariantCulture, typeof(T), false, defaultValue);
    }

    public static T ToValue<T>(this object obj)
    {
        return (T)ToValue(obj, CultureInfo.InvariantCulture, typeof(T), true, default);
    }

    public static bool TryConvert(this object obj, Type targetType, out object value)
    {
        try
        {
            value = ToValue(obj, CultureInfo.InvariantCulture, targetType, true, default);
            return true;
        }
        catch
        {
            value = null;
            return false;
        }
    }

    public static bool TryConvert<T>(this object obj, out T value, bool exceptionIfNotConvertable = false)
    {
        try
        {
            value = ToValue<T>(obj);
            return true;
        }
        catch
        {
            value = default;
            if (exceptionIfNotConvertable)
            {
                throw;
            }
            return false;
        }
    }

    private static object ToValue(
        object value, CultureInfo culture, Type targetType, bool exceptionIfConvertFailed, object defaultValue)
    {
        if (targetType == typeof(string) && value == null) return null;

        if (_IsNullable(targetType))
        {
            if (value == null) return null;
            if (string.IsNullOrEmpty(value.ToString())) return null;
        }

        if (value == null)
        {
            if (exceptionIfConvertFailed)
            {
                throw new ArgumentNullException("value");
            }
            else
            {
                return defaultValue;
            }
        }

        object resultValue;

        if (targetType == typeof(object)) return value;

        if (_TryConvert(value, culture, targetType, out resultValue)) return resultValue;

        if (exceptionIfConvertFailed)
        {
            throw new ArgumentException(
                "Can not convert '" + value.GetType().Name + "' to '" + targetType.Name + "'");
        }
        else
        {
            return defaultValue;
        }
    }

    private static bool _TryConvert(object value, CultureInfo culture, Type targetType, out object convertedValue)
    {
        return _TryAction(() =>
            _Convert(value, culture, targetType), out convertedValue);
    }

    private static bool _TryAction<T>(Func<T> creator, out T output)
    {
        try
        {
            output = creator();
            return true;
        }
        catch
        {
            output = default(T);
            return false;
        }
    }

    private static object _Convert(object value, CultureInfo culture, Type targetType)
    {
        if (value == null) throw new ArgumentNullException("value");

        if ((targetType.IsInterface || targetType.IsGenericTypeDefinition) || targetType.IsAbstract)
        {
            throw new ArgumentException("Target type {0} is not a value type or a non-abstract class.");
        }

        if (_IsNullableType(targetType)) targetType = Nullable.GetUnderlyingType(targetType);

        Type t = value.GetType();

        if (targetType == t) return value;

        if (t.IsSubclassOf(targetType)) return value;

        if ((value is string) && typeof(Type).IsAssignableFrom(targetType)) return Type.GetType((string)value, true);

        if ((value is IConvertible) && typeof(IConvertible).IsAssignableFrom(targetType))
        {
            if (targetType.IsEnum)
            {
                if (value is string)
                {
                    return Enum.Parse(targetType, value.ToString(), true);
                }
                if (_IsInteger(value))
                {
                    return Enum.ToObject(targetType, value);
                }
            }
            return System.Convert.ChangeType(value, targetType, culture);
        }

        if ((value is DateTime) && (targetType == typeof(DateTimeOffset))) return new DateTimeOffset((DateTime)value);

        if (value is string)
        {
            if (targetType == typeof(Guid))
            {
                return new Guid((string)value);
            }
            if (targetType == typeof(Uri))
            {
                return new Uri((string)value);
            }
            if (targetType == typeof(TimeSpan))
            {
                return TimeSpan.Parse((string)value);
            }
        }

        return System.Convert.ChangeType(value, targetType);
    }

    private static bool _IsInteger(object value)
    {
        switch (System.Convert.GetTypeCode(value))
        {
            case TypeCode.SByte:
            case TypeCode.Byte:
            case TypeCode.Int16:
            case TypeCode.UInt16:
            case TypeCode.Int32:
            case TypeCode.UInt32:
            case TypeCode.Int64:
            case TypeCode.UInt64:
                return true;
        }
        return false;
    }

    private static bool _IsNullable(Type t)
    {
        if (t == null) throw new ArgumentNullException("t");
        if (t.IsValueType)
        {
            return _IsNullableType(t);
        }
        return true;
    }

    private static bool _IsNullableType(Type t)
    {
        if (t == null) throw new ArgumentNullException("t");
        return (t.IsGenericType && (t.GetGenericTypeDefinition() == typeof(Nullable<>)));
    }

}
